'use strict';
const monk = require('monk');
const { getConfig } = require('./lib/helper');

const mongoConf = getConfig('mongoose');
const monkDb = monk(mongoConf.url);
const _ = require('lodash');

// 封装的模型类，里面包含了最基础的增删改查，改进的分页、无则增等方法
class CollectionModel {
  constructor(collectionName) {
    this.collection = monkDb.get(collectionName);
    this.collectionName = collectionName;
  }

  async aggregate(pipe = [], opt = {}, cb = null) {
    return this.collection.aggregate(pipe, opt, cb);
  }

  async checkFilter(filter) {
    const res = await this.collection.find(filter);
    if (res.length > 1) throw Error(`筛选条件${this.formatErrMsg(filter)}有误，在 ${this.collectionName} 中找到多条数据`);
  }

  // 新增
  async add(data) {
    const res = await this.collection.insert(Object.assign({}, data));
    return res;
  }

  // 删除
  async del(filter) {
    await this.checkFilter(filter);
    const { result } = await this.collection.remove(filter);
    return result;
  }

  // 修改
  async update(filter, data = {}, opt = {}) {
    await this.checkFilter(filter);
    await this.collection.update(filter, Object.assign({}, { $set: data }, opt));
    const res = await this.collection.findOne(filter);
    return res;
  }

  // 覆盖
  async cover(filter, data) {
    await this.checkFilter(filter);
    const res = await this.collection.update(filter, data);
    return res;
  }

  // 查找一个结果，无结果不报错
  async detail(filter = {}) {
    await this.checkFilter(filter);
    const res = await this.collection.find(filter);
    return res[0];
  }

  formatErrMsg(msg) {
    if ([ '[object Object]', '[object Array]' ].indexOf(Object.prototype.toString.call(msg) > -1)) msg = JSON.stringify(msg);
    return msg;
  }

  // 获取第一条数据
  async first(filter = {}, opt = {}) {
    const res = await this.collection.find(filter, opt);
    return res[0];
  }

  // 获取一条数据的严格方法，没有取到数据就无法进行后续操作时使用这个方法
  async getOne(filter) {
    const res = await this.collection.find(filter);

    if (res.length > 1) throw Error(`筛选条件${this.formatErrMsg(filter)}有误，在 ${this.collectionName} 中找到多条数据`);
    if (res.length === 0) throw Error(`筛选条件${this.formatErrMsg(filter)}有误，在 ${this.collectionName} 中找不到数据`);
    return res[0];
  }

  // 分页
  async pagination(filter = {}, opt = {}, perPage = 20, page = 1) {
    const collection = this.collection;
    // 获取列表
    const list = await collection.find(filter, Object.assign({
      limit: +perPage,
      skip: +perPage * (+page - 1),
    }, opt));
    const total = await collection.count(filter);
    return {
      list,
      pagination: {
        page: +page,
        perPage: +perPage,
        total,
      },
    };
  }

  // 获取完整列表（非分页）
  async all(filter = {}, opt = {}) {
    const collection = this.collection;
    // 获取列表
    const list = await collection.find(filter, opt);
    return list;
  }

  // 计数
  async count(filter = {}) {
    const collection = this.collection;
    const count = await collection.count(filter);
    return count;
  }

  // 如果无则新增，有则返回数据
  async addOrPass(filter, data = {}) {
    let entity = await this.collection.findOne(filter);
    if (entity) return entity;
    entity = await this.add(_.merge(filter, data));
    return entity;
  }

  // 如果无则新增, 有则更新
  async updateOrAdd(filter, data, initData = {}) {
    let entity = await this.collection.findOne(filter);
    if (entity) {
      entity = await this.update(filter, data);
    } else {
      entity = await this.add(_.merge(initData, data, filter));
    }
    return entity;
  }

  // 批量添加
  async batchAdd(data = []) {
    const insertData = data.map(item => Object.assign({}, item));
    const res = await this.collection.insert(insertData);
    return res;
  }

  // 批量修改
  async batchUpdate(filter, data = {}, opt = {}) {
    const updateResult = await this.collection.update(filter, Object.assign({}, { $set: data }, opt), { multi: true });
    return updateResult;
  }

  // 批量删除
  async batchDel(filter) {
    if (_.isString(filter) && filter.length === 24) {
      filter = { _id: filter };
    }
    const res = await this.collection.remove(filter);
    return res;
  }

  // 检查是否存在
  async exist(filter, opt = {}) {
    if (_.isString(filter) && filter.length === 24) {
      filter = {
        _id: filter,
      };
    }
    const res = await this.collection.find(filter, opt);
    return res.length > 0;
  }

  // 查看某个值的最大值
  async max(field, filter) {
    const res = await this.collection.findOne(filter, {
      sort: { [field]: -1 },
      skip: 0,
      limit: 1,
    });
    return res;
  }

  // 查看某个值的最小值
  async min(field, filter) {
    const res = await this.collection.findOne(filter, {
      sort: { [field]: 1 },
      skip: 0,
      limit: 1,
    });
    return res;
  }

  // 字段递增
  async increase(filter, incs) {
    const res = await this.update(filter, {}, { $inc: incs });
    return res;
  }

  // 字段批量递增
  async batchIncrease(filter, incs) {
    const res = await this.batchUpdate(filter, {}, { $inc: incs });
    return res;
  }

  // 检查传入的id数组是否存在
  async checkExist(ids, errorMsg = '数据不存在', throwError = true) {
    if (Object.prototype.toString.call(ids) !== '[object Array]') ids = [ ids ];
    const count = await this.count({ _id: { $in: ids } });
    const allExist = count === ids.length;
    if (throwError && !allExist) throw Error(errorMsg);
    return allExist;
  }

  // 计算某个字段的总和
  async sum(filter, field) {
    const handle = await this.aggregate([{
      $match: filter,
    }, {
      $group: {
        _id: null,
        sum: { $sum: `$${field}` },
      },
    }]);
    const sum = handle.reduce((t, e) => t + e.sum, 0);
    return sum;
  }
}
module.exports = {
  monk: monkDb,
  monkCollection(collectionName) {
    const collection = monkDb.get(collectionName);
    return collection;
  },
  ObjectId: monkDb.id,
  collectionModel(collectionName) {
    if (!collectionName) {
      throw Error(`mongoUtil: 传入集合名 ${collectionName} 不正确`);
    }
    return new CollectionModel(collectionName);
  },
};
